package org.expressme.ant;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.List;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;

/**
 * Generate classes of Action Script from JavaBeans.
 * 
 * @author Michael Liao (askxuefeng@gmail.com)
 */
public class JavaBean2ActionScriptTask extends Task {

    private String packageName;
    private boolean orderByName = false;
    private String includes = "*";
    private File outputDir;
    private String encoding = "UTF-8";

    public JavaBean2ActionScriptTask() {
        init();
    }

    @Override
    public void execute() throws BuildException {
        if (packageName==null || packageName.length()==0)
            throw new BuildException("Attribute 'packageName' must be specified.");
        if (outputDir==null)
            throw new BuildException("Attribute 'outputDir' must be specified.");
        if (!outputDir.isDirectory())
            throw new BuildException("Directory '" + outputDir + "' not exist.");
        final String reg = pattern2Regex(includes);
        List<Class<?>> list = new ClassPathScanner(
                packageName,
                new ClassFilter() {
                    public boolean accept(Class<?> clazz) {
                        String p = clazz.getPackage().getName();
                        return p.equals(packageName) && clazz.getSimpleName().matches(reg);
                    }
                }
        ).scan();
        generateActionScript(list);
    }

    String pattern2Regex(String pattern) {
        if (pattern==null || pattern.isEmpty())
            return "^.*$";
        StringBuilder sb = new StringBuilder(32);
        sb.append("^");
        for (int i=0; i<pattern.length(); i++) {
            char c = pattern.charAt(i);
            if (c=='*')
                sb.append(".*");
            else if (c=='?')
                sb.append(".{1}");
            else if (c>='a' && c<='z')
                sb.append(c);
            else if (c>='A' && c<='Z')
                sb.append(c);
            else if (c>='0' && c<='9')
                sb.append(c);
            else
                sb.append('\\').append(c);
        }
        sb.append("$");
        return sb.toString();
    }

    void generateActionScript(List<Class<?>> list) throws BuildException {
        for (Class<?> clazz : list) {
            generateActionScript(clazz);
        }
    }

    void generateActionScript(Class<?> clazz) {
        String p = clazz.getPackage().getName().replace('.', '/');
        String fullPath = outputDir.getPath() + "/" + p;
        File dir = new File(fullPath);
        if (! dir.isDirectory()) {
            if (! dir.mkdirs())
                throw new BuildException("Could not create directory: " + dir.getPath());
        }
        fullPath = fullPath + "/" + clazz.getSimpleName() + ".as";
        log("Generating " + clazz.getName() + " to '" + fullPath + "'...");
        JavaBean2ActionScript j2a = new JavaBean2ActionScript();
        String content = j2a.toActionScript(clazz, orderByName);
        writeToFile(content, new File(fullPath));
    }

    void writeToFile(String content, File outputFile) {
        // write to file:
        BufferedWriter w = null;
        try {
            w = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFile), encoding));
            w.write(content);
        }
        catch (IOException e) {
            log("Cannot write file: " + outputFile.getPath(), e, Project.MSG_ERR);
            throw new BuildException(e);
        }
        finally {
            if (w!=null) {
                try {
                    w.close();
                }
                catch(IOException e) {
                    throw new BuildException(e);
                }
            }
        }
    }

    public void setPackageName(String packageName) {
        this.packageName = packageName;
    }

    public void setOrderByName(boolean orderByName) {
        this.orderByName = orderByName;
    }

    public void setIncludes(String includes) {
        this.includes = includes;
    }

    public void setOutputDir(File outputDir) {
        this.outputDir = outputDir;
    }

    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }
}
